package com.gitee.wsl.ext.base

import com.gitee.wsl.base.Overlap

val LONG_ZERO_TO_MAX_RANGE = 0L..Long.MAX_VALUE

fun IntRange.intersect(other: IntRange): IntRange = kotlin.math.max(
    first,
    other.first
)..kotlin.math.min(last, other.last)

val IntRange.size: Int
    get() = last - first + 1

fun IntRange.getValues(start:Int,count:Int,step:Int=1) = (1..count).mapIndexed {i,_-> start + step * i }

operator fun IntRange.contains(other: IntRange): Boolean = (other.first in this) && (other.last in this)

operator fun LongRange.contains(other: LongRange): Boolean =
    other.first >= start && other.last <= endInclusive


val IntRange.length: Int get() = (this.last - this.first) + 1

val LongRange.length: Long get() = (this.last - this.first) + 1

inline val CharRange.size: Int get() = last - first + 1
inline val UIntRange.size: Long get() = last.toLong() - first.toLong() + 1L
inline val LongRange.size: Long get() = (this.last - this.first) + 1
inline val ULongRange.size: ULong get() = (this.last - this.first) + 1U


fun IntRange.toNonEmptyRange(): IntRange {
    if (length <= 0) {
        return start .. start
    }
    return this
}

fun IntRange.toLongRange() = this.first.toLong()..this.last.toLong()

val IntRange.endExclusiveWrapped: Int get() = this.last + 1
val LongRange.endExclusiveWrapped: Long get() = this.last + 1
val IntRange.endExclusiveClamped: Int get() = if (this.last == Int.MAX_VALUE) Int.MAX_VALUE else this.last + 1
val LongRange.endExclusiveClamped: Long get() = if (this.last == Long.MAX_VALUE) Long.MAX_VALUE else this.last + 1

operator fun IntRange.inc(): IntRange {
    return start + 1..endInclusive + 1
}

operator fun IntRange.dec(): IntRange {
    return start - 1..<endInclusive
}

val IntRange.Companion.Infinite get() = Int.MIN_VALUE..Int.MAX_VALUE

val LongRange.Companion.Infinite get() = Long.MIN_VALUE..Long.MAX_VALUE

fun IntRange.hasIntersectionWith(other: IntRange): Boolean {
    return (this.first <= other.first && this.last >= other.first)
            || (other.first <= this.first && other.last >= this.first)
}

val IntRange.center: Int get() = (first + last) / 2

fun IntRange.containsAndNotOnEdges(value: Int): Boolean =
    value > this.first && value < this.last

fun IntRange.containsAndNotOnEdges(value: IntRange): Boolean =
    containsAndNotOnEdges(value.first) && containsAndNotOnEdges(value.last)

fun IntRange.addPosition(index: Int): IntRange =
    if (index <= first) {
        IntRange(first + 1, last + 1)
    } else if (index <= last) {
        IntRange(first, last + 1)
    } else {
        this
    }

fun IntRange.add(rangeToAdd: IntRange): List<IntRange> {
    return if (rangeToAdd.first < this.first) {
        listOf(IntRange(this.first + rangeToAdd.length, this.last + rangeToAdd.length))
    } else if (rangeToAdd.first == this.first) {
        (rangeToAdd.first..(rangeToAdd.last + 1))
            .map { it..(this.last + rangeToAdd.length) }
    } else if (rangeToAdd.first > this.first && rangeToAdd.first <= this.last) {
        listOf(IntRange(this.first, this.last + rangeToAdd.length))
    } else if (rangeToAdd.first == this.last + 1) {
        (0..rangeToAdd.length).map { this.first..(this.last + it) }
    } else {
        listOf(this)
    }
}

fun IntRange.remove(rangeToRemove: IntRange): List<IntRange> {
    return if (rangeToRemove.last < this.first) {
        listOf(IntRange(this.first - rangeToRemove.length, this.last - rangeToRemove.length))
    } else if (rangeToRemove.first > this.last) {
        listOf(this)
    } else if (rangeToRemove.first <= this.first && rangeToRemove.last >= this.last) {
        emptyList()
    } else if (rangeToRemove.first >= this.first && rangeToRemove.last <= this.last) {
        listOf(IntRange(this.first, this.last - rangeToRemove.length))
    } else if (rangeToRemove.first <= this.first && rangeToRemove.last > this.first) {
        listOf(IntRange(rangeToRemove.first, this.last - rangeToRemove.length))
    } else if (rangeToRemove.first <= this.last && rangeToRemove.last > this.last) {
        listOf(IntRange(this.first, rangeToRemove.first - 1))
    } else {
        listOf(this)
    }
}

fun IntRange.addToRangeAndWiden( t: Int): IntRange {
    return IntRange(first + t, last + t + 1)
}


/**
 * @return a progression from this value to the specified [to] value, inclusive
 */
infix fun Int.to(to: Int): IntProgression
{
    val step = if (this > to) -1 else 1
    val start = if (this > to) this - 1 else this + 1
    return IntProgression.fromClosedRange(start, to, step)
}

/**
 * @return a progression from this value to the specified [to] value, exclusive
 */
infix fun Int.toExclusive(to: Int): IntProgression
{
    val step = if (this > to) -1 else 1
    val start = if (this > to) this - 1 else this + 1
    val end = if (this > to) to + 1 else to - 1
    return IntProgression.fromClosedRange(start, end, step)
}

/**
 * Determines [Overlap] case for two ranges by comparing their values.
 */
fun IntRange.overlap(target: IntRange): Overlap = when {
    first == target.first && last == target.last -> Overlap.EQUAL
    last < target.first -> Overlap.BEFORE
    first > target.last -> Overlap.AFTER
    first <= target.first && last >= target.last -> Overlap.OUTER
    first > target.first && last < target.last -> Overlap.INNER
    first == target.first && last < target.last -> Overlap.INNER_LEFT
    first > target.first && last == target.last -> Overlap.INNER_RIGHT
    target.first in (first + 1)..last -> Overlap.LEFT
    else -> Overlap.RIGHT
}

operator fun ClosedRange<Float>.times(value: Float) = (start * value)..(endInclusive * value)
operator fun ClosedRange<Float>.div(value: Float) = (start / value)..(endInclusive / value)

/**
 * Returns a new [IntRange] with the specified [offset] added to both the start and end of the range.
 * @param offset the amount to offset the range by
 * @return a new [IntRange] with the offset applied
 */
fun IntRange.offset(offset: Int): IntRange = IntRange(first + offset, last + offset)
